/*Make:gcc main.c -lpcap -g -Wall*/

#include <pcap.h>
#include "pcap-int.h"

/* From tcptraceroute */
#define IPTOSBUFFERS	12
static char *iptos(bpf_u_int32 in)
{
	static char output[IPTOSBUFFERS][3*4+3+1];
	static short which;
	u_char *p;

	p = (u_char *)&in;
	which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1);
	sprintf(output[which], "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
	return output[which];
}

char *getdev(void)
{
	char *dev=NULL;
	char ebuf[PCAP_ERRBUF_SIZE]={0};
	pcap_if_t *alldevs;
	pcap_if_t *d;
	bpf_u_int32 net, mask;
	
	/*返回可用的网口列表*/
	if (pcap_findalldevs(&alldevs, ebuf) == -1){
		printf("pcap_findalldevs: %s\n",ebuf);
		return NULL;
	}
	printf("Can use dev list:");
	for(d=alldevs;d;d=d->next){
		printf("%s ",d->name);
	}
	printf("\n");
	
	/*返回可用的第一个网口*/
	if ((dev = pcap_lookupdev(ebuf))==NULL){
		printf("pcap_lookupdev:%s\n",ebuf);
		return NULL;
	}
	/*查询子网掩码*/
	if (pcap_lookupnet(dev, &net, &mask, ebuf) < 0){
		printf("pcap_lookupnet:%s\n",ebuf);
		return NULL;
	}
	printf("dev[%s] is on network: %s/%s\n",dev,iptos(net), iptos(mask));
	
	return dev;
}

pcap_t *create_and_set(char *dev)
{
	pcap_t *pd;
	int status=0;
	char ebuf[PCAP_ERRBUF_SIZE]={0};
	int snaplen=2048;
	int timeout=60000;
	int size= 40*1024*1024;
	
	if((pd = pcap_create(dev, ebuf))==NULL){
		printf("pcap_create:%s\n",ebuf);
		return NULL;
	}
	/*设置捕包最大大小，注意最终会受限于MTU*/
	if((status = pcap_set_snaplen(pd, snaplen))){
		printf("pcap_set_snaplen:%s\n", pcap_statustostr(status));
		return NULL;
	}
	/*设置为混杂模式，注意在"any"接口下混杂模式不可生效*/
	if((status = pcap_set_promisc(pd, 1))){
		printf("pcap_set_promisc:%s\n", pcap_statustostr(status));
		return NULL;
	}
	/*设置为监控模式，是否能设置和具体的os类型有关*/
	if(pcap_can_set_rfmon(pd)&&(status = pcap_set_rfmon(pd, 1))){
		printf("pcap_set_rfmon:%s\n", pcap_statustostr(status));
		return NULL;
	}
	/*设置超时返回时间（ms） FIXME*/
	if((status = pcap_set_timeout(pd, timeout))){
		printf("pcap_set_timeout:%s\n", pcap_statustostr(status));
		return NULL;
	}
	/*设置可用于缓存报文的总空间大小，默认值是2M*/
	if((status = pcap_set_buffer_size(pd, size))){
		printf("pcap_set_buffer_size:%s\n", pcap_statustostr(status));
		return NULL;
	}
	/*others....*/
	/*pcap_list_tstamp_types(pd,&type);*/
	
	/*设置pd与一个socket fd关联，pd->fd*/
	if((status = pcap_activate(pd))){
		printf("pcap_activate:%s %s\n",pcap_statustostr(status), pcap_geterr(pd));
		return NULL;
	}
	return pd;
}

static void dosomething(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
{
	/*统计每个pcap_dispatch调用中，dosomething函数执行的次数*/
	int *counterp = (int *)user;
	(*counterp)++;
}


int dodispatch(pcap_t *pd)
{
	int status;
	unsigned int packet_count;

	for (;;) {
		packet_count = 0;
		status = pcap_dispatch(pd, -1, dosomething,(u_char *)&packet_count);
		if (status < 0)
			break;
		printf("%d packets seen, %d packets handled after pcap_dispatch returns\n",
				status, packet_count);
	}
	return 0;
}


int doselect(pcap_t *pd)
{
	int fd;

	if((fd = pcap_get_selectable_fd(pd))==-1){
		printf("pcap_get_selectable_fd error\n");		
		return -1;
	}

	for(;;){
	
	}

	return 0;
}

int dopoll(pcap_t *pd)
{
	int fd;

	if((fd = pcap_get_selectable_fd(pd))==-1){
		printf("pcap_get_selectable_fd error\n");		
		return -1;
	}
	for(;;){
	
	}

	return 0;
}

/*cooked:要对数据包进行加工(cooked)，把数据包的数据链路层头部去掉，并
 * 使用一个通用结构sockaddr_ll来保存链路信息。*/
/* 某些情况下链路层的结构可能不同，没法以统一的方式交给上层处理*/
int showinfo(pcap_t *pd)
{
	if(pd->md.cooked==0&&pd->md.sock_packet==1){
		/*旧式的捕包模式 linux2.0之前*/
		printf("Old Style: socket(PF_INET, SOCK_PACKET, htons(ETH_P_ALL))\n");
	}else if(pd->md.sock_packet==0&&pd->md.cooked==0){
		/*新式捕包模式，无cooked，libpcap会优先选择尝试使用这种模式*/
		printf("New Style dont cook: "
			"socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))\n");
	}else if(pd->md.sock_packet==0&&pd->md.cooked==1){
		/*新式捕包模式，有cooked*/
		printf("New Style with cook: "
			"socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL))\n");
	}else{
		printf("Unknown Style\n");
	}
	if(pd->md.mmapbuf){
		/*mmap的模式能避免数据从内核态拷贝到用户态，和避免大量系统调用，
		 * libpcap会优先选择尝试使用这种模式
		 * 注意这并不是完全的零拷贝模式*/
		printf("Mmap Mode[ring size:%d]\n",pd->md.mmapbuflen);
	}else{
		/*copy模式，使用标准的recv socket接口把包拷贝到用户态*/
		printf("Copy Mode[buffer size:%d]\n",pd->bufsize);
	}
	return 0;
}

int main()
{
	char *dev;
	pcap_t *pd;
	
	dev=getdev();
	pd=create_and_set(dev);
	showinfo(pd);
	
	getchar();
	
	dodispatch(pd);

	pcap_close(pd);

	return 0;
}

